#ifndef __VMCodeGenerator__h__included__
#define __VMCodeGenerator__h__included__

#include "neiderra/core/Types.h"
#include "neiderra/core/io/IOStream.h"
#include "neiderra/utils/Lexer.h"

#include <string>
#include <vector>
#include <map>

using namespace neiderra::types;
using namespace neiderra::utils::lexer;
using namespace neiderra::core;
using std::string;
using std::vector;
using std::map;

namespace neiderra {
	namespace vm {

		typedef map<string, unsigned> Symtable;

		class CodeGenerator: public Base {
		public:
			struct exBadToken{
				exBadToken(int ln): line(ln) {}
				int line;
			};
			struct exBadCommand{};
			struct exNotImplemented{};
			struct exBadOpSize{};
			struct exBadRegister{};
		public:
			CodeGenerator(Core*);
			virtual ~CodeGenerator();

			nByte* generate(iInputStream* input, unsigned& size);
		private:
			enum Command {
				cmdADD, 
				cmdSUB,	
				cmdDIV,	
				cmdMUL,	
				cmdPUSH,
				cmdPOP, 
				cmdTOP,
				cmdMOV, 
				cmdLD, 
				cmdCALL, 
				cmdPROC,
				cmdENDPROC,	
				cmdIN, 
				cmdDB, 
				cmdOUT, 
				cmdRET,	
				cmdBREQ, 
				cmdBRNEQ, 
				cmdNOP, 
				cmdINC,
				cmdDEC, 
				cmdGOTO, 
				cmdORG, 
				cmdCP, 
				cmdERR, 
				cmdPUSHM,
				cmdPOPM,
				cmdTOPM,
				cmdDBM,
				cmdSTM,
				cmdST			// st id, reg, size
			};

			//const char* op_name[] = {"add", "sub", "div", "mul", "push",
			//	"pop", "mov", "ld", "call", "proc", "endp", "in", "db",
			//	"out", "ret", "breq", "brneq", "nop", "inc", "dec", "goto",
			//	"org", "cp" };

			Command getCommand(const string&);
			void match(tokens t);
			bool next(tokens t);
			void writeByte(nByte);
			void writeBytes(nByte* bytes, unsigned size);
			void writeUInt(unsigned int);
			void writeInt(int);
			unsigned getRegNum(const string&);
			unsigned getParentOffset(const string&);
			
			// actually: count of '$' chars in the string 
			unsigned getAbsoluteOffset(const string&);
			
			void getId(string&);
			string extractParentName(const string&);
			
			iInputStream* _input;
            Lexer* _lexer;
			Token _ct;
			unsigned _counter;
			unsigned _mainpos;
			nByte* _bytecode;
			map<string, unsigned> _procs;
			map<string, vector<unsigned> > _procLabels;
			Symtable _symtab;
		};
	}
}

#endif
